﻿// This program draws triangulated surfaces and applies various drawing modes 
// to them.
// Copyright (C) 2003  Chris J. Friesen
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

/*  Name: Chris J. Friesen
 *  Class: CS310 - Graphics
 *  File: ModelLoader.cs
 *  Date: March 14, 2003.
 *  Purpose: Loads .obj files from numerous modeling programs.
 *  Bugs: Not that I can tell.
 *  Notes: Ported from ArRoNs java implementation.
 */

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

public class ModelLoader
{
    //The things that will be parsed for in the .obj
    private const string VERTICES = "v ";
    private const string TEXTURES = "vt ";
    private const string NORMALS  = "vn ";
    private const string FACES    = "f ";
    private const string MATERIAL = "usemtl ";

    private float[] myTextures,        //The texture data.
                    myVertices,        //The vertex data.
                    myNormals;         //The normals data.
    private int[]   myFaces;           //The face data.
    private int     myNumVertices,     //The number of vertex points.
                    myNumTextures,     //The number of texture points.
                    myNumNormals,      //The number of normal points.
                    myNumFaces;        //The number of face points.
	private float myMinX,			   //The minumum X,
	              myMinY,			   //			 Y,
	              myMinZ,			   //			 Z.
	              myMaxX,			   //The maximum X,
	              myMaxY,			   //            Y,
	              myMaxZ;              //			 Z.
    public string   myTextureFile;     //The texture file for the object.

    //Pre: None.
    //Post: None.
    //Purpose: To initialize.
    public ModelLoader()
    {
    	this.Reset();
    }
    
    //Pre: None.
    //Post: None.
    //Purpose: Sets all the variables to their defaults.
    public void Reset()
    {
    	this.textures = null;
    	this.vertices = null;
    	this.normals = null;
    	this.faces = null;
    	this.numVertices = 0;
    	this.numTextures = 0;
    	this.numNormals  = 0;
    	this.numFaces = 0;
    	this.minX  = 0;
    	this.minY = 0;
    	this.minZ = 0;
    	this.maxX = 0;
    	this.maxY = 0;
    	this.maxZ = 0;
    	this.name = null;
    }
    
    //Pre: A file name of a .obj to load.
    //Post: None.
    //Purpose: To read the data from a .obj and parse to so it can
    //         be drawn.  Even has error checking :)
    public void ReadFile(string filename)
    {
        StreamReader reader;
        string line;
        string[] exploded;
        
        float current;
        int axis;
        int i;

        try
        {
            reader = File.OpenText(filename);
            line = reader.ReadLine();

            while(line != null)
            {
                if(line.StartsWith(VERTICES))
                    this.numVertices++;
                else if(line.StartsWith(TEXTURES))
                    this.numTextures++;
                else if(line.StartsWith(NORMALS))
                    this.numNormals++;
                else if(line.StartsWith(FACES))
                    this.numFaces++;

                line = reader.ReadLine();
            }

            this.myVertices = new float[this.numVertices*3];
            this.myTextures = new float[this.numTextures*2];
            this.myNormals  = new float[this.numNormals*3];
            this.myFaces    = new int[this.numFaces*3*4];
            reader.Close();

            this.numFaces    = 0;
            this.numNormals  = 0;
            this.numTextures = 0;
            this.numVertices = 0;

            reader = File.OpenText(filename);
            line = reader.ReadLine();

            while(line != null)
            {
                if(line.StartsWith(VERTICES))
                {
                    exploded = line.Split(' ');
                    axis = 0;
                    for(i = 1; i < exploded.Length; i++)
                    {
                        current = float.Parse(exploded[i]);
                        this.vertices[this.numVertices] = current;
                        this.numVertices++;

                        switch(axis)
                        {
                            case 0 : this.minX = Math.Min(this.minX, current);
                                     this.maxX = Math.Max(this.maxX, current);
                                     break;
                            case 1 : this.minY = Math.Min(this.minY, current);
                                     this.maxY = Math.Max(this.maxY, current);
                                     break;
                            case 2 : this.minZ = Math.Min(this.minZ, current);
                                     this.maxZ = Math.Max(this.maxZ, current);
                                     break;
                        }
                        axis++;
                    }
                }
                else if(line.StartsWith(TEXTURES))
                {
                    exploded = line.Split(' ');
                    
                    for(i = 1; i < exploded.Length; i++)
                    {
                        current = float.Parse(exploded[i]);
                        this.textures[this.numTextures] = current;
                        this.numTextures++;
                    }
                }
                else if(line.StartsWith(NORMALS))
                {
                    exploded = line.Split(' ');

                    for(i = 1; i < exploded.Length; i++)
                    {
                        current = float.Parse(exploded[i]);
                        this.normals[this.numNormals] = current;
                        this.numNormals++;
                    }
                }
                else if(line.StartsWith(FACES))
                {
                    string[] exploded2;
                    int j;

                    exploded = line.Split(' ');

                    for(i = 1; i < exploded.Length; i++)
                    {
                        exploded2 = exploded[i].Split('/');

                        for(j = 0; j < exploded2.Length; j++)
                        {
                            if(exploded2[j] == "")
                            {
                                current = int.Parse(exploded2[j+1]);
                            }
                            else
                            {
                                current = int.Parse(exploded2[j]);
                            }

                            this.faces[this.numFaces] = (int) current;
                            this.numFaces++;
                        }
                    }
                }
                else if(line.StartsWith(MATERIAL))
                {
                    exploded = line.Split(' ');
                    this.name = exploded[1];
                }

                line = reader.ReadLine();
            }
            reader.Close();
        }
        catch (Exception)
        {
            MessageBox.Show("There was a problem reading file: " + filename);
        	//filename = CJF.Dialogs.DoBrowseDialog("data/Objects", "obj", filename);

            if(filename == "")
                return;

        	this.ReadFile(filename);
        }
    }

    //** Accessor Methods **//

    //Pre: To set the texture array.
    //Post: To get the texture array.
    //Purpose: To get or set the texture array.
    public float[] textures
    {
        get { return this.myTextures;  }
        set { this.myTextures = value; }
    }

    //Pre: To set the vertices array.
    //Post: To get the vertices array.
    //Purpose: To get or set the vertices array.
    public float[] vertices
    {
        get { return this.myVertices;  }
        set { this.myVertices = value; }
    }

    //Pre: To set the normals array.
    //Post: To get the normals array.
    //Purpose: To get or set the normals array.
    public float[] normals
    {
        get { return this.myNormals;  }
        set { this.myNormals = value; }
    }

    //Pre: To set the faces array.
    //Post: To get the faces array.
    //Purpose: To get or set the faces array.
    public int[] faces
    {
        get { return this.myFaces;  }
        set { this.myFaces = value; }
    }

    //Pre: To set the number of vertices.
    //Post: To get the number of vertices.
    //Purpose: To set or get the number of vertices.
    public int numVertices
    {
        get { return this.myNumVertices;  }
        set { this.myNumVertices = value; }
    }

    //Pre: To set the number of textures.
    //Post: To get the number of textures.
    //Purpose: To set or get the number of textures.
    public int numTextures
    {
        get { return this.myNumTextures;  }
        set { this.myNumTextures = value; }
    }

    //Pre: To set the number of normals.
    //Post: To get the number of normals.
    //Purpose: To set or get the number of normals.
    public int numNormals
    {
        get { return this.myNumNormals;  }
        set { this.myNumNormals = value; }
    }

    //Pre: To set the number of faces.
    //Post: To get the number of faces.
    //Purpose: To set or get the number of faces.
    public int numFaces
    {
        get { return this.myNumFaces;  }
        set { this.myNumFaces = value; }
    }

	public float minX
	{
		get { return this.myMinX;  }
		set { this.myMinX = value; }
	}
	
	public float minY
	{
		get { return this.myMinY;  }
		set { this.myMinY = value; }
	}
	
	public float minZ
	{
		get { return this.myMinZ;  }
		set { this.myMinZ = value; }
	}
	
	public float maxX
	{
		get { return this.myMaxX;  }
		set { this.myMaxX = value; }
	}
	
	public float maxY
	{
		get { return this.myMaxY;  }
		set { this.myMaxY = value; }
	}
	
	public float maxZ
	{
		get { return this.myMaxZ;  }
		set { this.myMaxZ = value; }
	}

    //Pre: To set the name.
    //Post: To get the name.
    //Purpose: To set or get the name.
    public string name
    {
        get { return this.myTextureFile;  }
        set { this.myTextureFile = value; }
    }
}
